/********************************************************************************
 * Project Name		[JavaEE_Web]
 * File Name     	[FileUploadAndDownload.java]
 * Creation Date 	[2015-01-01]
 * 
 * Copyright© ge.y.yang@gmail.com All Rights Reserved
 * 
 * Work hard, play harder, think big and keep fit
 ********************************************************************************/
package _notes.servlet;

/**
 * 文件上传下载<br/>
 * 
 * @author 不落的太阳(Sean Yang aka ShortPeace)
 * @version 1.0
 * @since jdk 1.8
 * 
 */
public final class FileUploadAndDownload {

	public void fileUploadSteps() {
		/**
		 * 文件上传<br/>
		 * 
		 * 准备<br/>
		 * 1. 使用file的表单域:<input type="file" name="file"/><br/>
		 * 2. 请求方式为POST<br/>
		 * 3. 使用 multipart/form-data的请求编码方式<br/>
		 * <code>
		 * <form action="${pageContext.request.contextPath}/uploadServlet" method="post" enctype="multipart/form-data">
		 * </code><br/>
		 * 
		 * enctype属性: 指定将数据发送到服务器时浏览器使用的编码类型<br/>
		 * enctype属性取值:<br/>
		 * 1. application/x-www-form-urlencoded: 表单enctype属性的默认值,
		 * 这种编码方案使用有限的字符集, 当使用了非字母和数字时, 必须用”%XX”代替[X代表十六进制数字],
		 * 对于大容量的二进制数据或包含非ASCII字符的文本来说, 这种编码不能满足要求<br/>
		 * 2. multipart/form-data: 表示表单以二进制传输数据<br/>
		 * 
		 * 服务端<br/>
		 * 1. 不能再使用request.getParameter()方式获取请求信息, 获取不到,
		 * 因为请求的编码方式已经改为multipart/form-data, 以 二进制的方式来提交请求信息<br/>
		 * 2. 可以使用输入流的方式来获取, 但不建议这样做<br/>
		 * 3. 使用其他组件来完成文件的上传操作<br/>
		 */
	}

	public void commonnsFileupload() {
		/**
		 * 使用commons.fileupload组件<br/>
		 *
		 * 1. 搭建环境:加入两个jar<br/>
		 * commons-fileupload-1.3.1.jar<br/>
		 * commons-io-2.4.jar<br/>
		 * 
		 * 2. 使用<br/>
		 * 
		 * commons-fileupload可以解析请求, 得到一个FileItem对象组成的 List<br/>
		 * commons-fileupload把所有的请求信息都解析为FileItem对象, 无论是一个一般的文本域还是一个文件域,
		 * 可以调用FileItem的isFormField()方法来判断是一个表单域或不是表单域[是一个文件域]<br/>
		 * 
		 * <code>
		 * <pre>
		 * // 得到FileItem的集合, 基于该集合创建一个DiskFileItemFactory
		 * DiskFileItemFactory factory = new DiskFileItemFactory();
		 * 
		 * // 设置factory的属性
		 * // 设置内存中最多可以存放的上传文件的字节数, 若超出则把文件写到一个临时文件夹中. 以byte为单位
		 * factory.setSizeThreshold(1024 * 500);
		 * factory.setRepository(new File("Z:/temp"));
		 * 
		 * // 创建一个上传处理器
		 * ServletFileUpload upload = new ServletFileUpload(factory);
		 * 
		 * // 设置upload的属性
		 * // 设置单个文件最大字节数
		 * fileUpload.setFileSizeMax(maxFileSize);
		 * // 设置总共允许上传的最大字节数
		 * fileUpload.setSizeMax(totalMaxSize);
		 * // 设置编码
		 * fileUpload.setHeaderEncoding("UTF-8");
		 * // 设置进度监听器
		 * fileUpload.setProgressListener(new ProgressListener() {
		 *     public void update(long bytesRead, long contentLength, int item) {
		 *         System.out.println("第" + (item + 1) + "个文件上传, 大小为  " + contentLength + ", 已经上传 " + bytesRead);
		 *     }
		 * });
		 * 
		 * try {
		 *     // 解析Request, 得到所有的表单域
		 *     List<FileItem> items = upload.parseRequest(request);
		 *     
		 *     // 遍历
		 *     for (FileItem item : items) {
		 *         // 如果是一个普通的表单域
		 *         if (item.isFormField()) {
		 *             String fieldName = item.getFieldName();
		 *             String fieldValue = item.getString();
		 *         } else {
		 *             // 如果是文件域
		 *             String fieldName = item.getFieldName();
		 *             String fileName = item.getName();
		 *             // String contentType = item.getContentType();
		 *             // long fileSize = item.getSize();
		 *             
		 *             InputStream uploadStream = item.getInputStream();
		 *             byte[] buffer = new byte[1024];
		 *             int length = 0;
		 *             OutputStream outputStream = new FileOutputStream(new File(factory.getRepository() + File.separator + fileName));
		 *             
		 *             while ((length = uploadStream.read(buffer)) != -1) {
		 *                 outputStream.write(buffer, 0, length);
		 *             }
		 *             outputStream.close();
		 *             uploadStream.close();
		 *         }
		 *     }
		 * } catch (FileUploadException e) {
		 *     e.printStackTrace();
		 * }
		 * </pre>
		 * </code>
		 * 
		 * 主要的API<br/>
		 * ServletFileUpload: 包含上传处理的多个方法<br/>
		 * FileUploadBase: 包含了一些文件上传中可能出现的异常<br/>
		 */
	}

	public void servletAPIUpload() {
		/**
		 * 使用Servlet 3.0 API上传文件<br/>
		 * 
		 * Servlet 3.0定义了一个 @MultipartConfig 注解, 该注解标注的Servlet可以实现文件上传功能,
		 * 注解 @MultipartConfig 要放在注解 @WebServlet 之前<br/>
		 * Servlet 3.0将multipart/form-data的post请求封装成为一个Part, 通过Part对上传的文件进行操作
		 * <br/>
		 * 
		 * <code>
		 * <pre>@MultipartConfig(
		 *      	fileSizeThreshold = 1024 * 500 	// int类型, 可选参数, 当数量超过该属性设置值时, 内容将被写入文件
		 *      	location = "z:/temp"			// String类型, 可选参数, 存放上传文件的地址
		 *      	maxFileSize = 1048576			// long类型, 可选参数, 允许上传文件的最大字节数, 默认值为-1, 表示无限制
		 *      	maxRequestSize = 542880			// long类型, 可选参数, 针对该 multipart/form-data请求的最大字节数, 默认值为 -1, 表示无限制
		 *      )
		 * </pre>
		 * </code><br/>
		 * 
		 * 最佳实践<br/>
		 * 1. 若是上传一个文件, 仅仅需要设置maxFileSize熟悉即可<br/>
		 * 2. 上传多个文件, 需要设置maxRequestSize属性, 设定一次上传数据的最大量<br/>
		 * 3. 上传过程中无论是单个文件超过maxFileSize值, 还是上传总的数据量大于maxRequestSize值,
		 * 都会抛出IllegalStateException异常<br/>
		 * 4. location属性, 既是保存路径, 又是上传过程中临时文件的保存路径, 一旦执行Part.write()方法之后,
		 * 临时文件将被自动清除<br/>
		 * 5. Servlet 3.0 API, 不提供获取上传文件名的方法,
		 * 可以通过part.getHeader("content-disposition")方法间接获取得到<br/>
		 * 
		 * 读取注解 @MultipartConfig 的属性值<br/>
		 * <code>
		 * private static final MultipartConfig config;
		 * 
		 * static {
		 *     config = FileUploadServlet.class.getAnnotation(MultipartConfig.class);
		 *     String location = config.location();
		 *     File file = new File(location);
		 *     if (!file.exists()) {
		 *         file.mkdirs();
		 *     }
		 *     // 在doPost方法里面可以读取MultiConfig的各种配置信息对文件大小, 文件数量等做检查
		 * }
		 * </code><br/>
		 */
	}

	public void downloadFile() {
		/**
		 * 文件下载<br/>
		 * 
		 * 步骤<br/>
		 * 1. 设置contentType响应头,
		 * response.setContentType("application/x-msdownload"); <br/>
		 * 2. 设置content-disposition响应头, 通知浏览器不再由浏览器来自行处理[或打开]要下载的文件, 而由用户手工完成
		 * response.setHeader("content-disposition",
		 * "attachment;filename=1.txt"); <br/>
		 * 3. 具体的文件: 可以调用response.getOutputStream的方式, 以IO流的方式发送给客户端<br/>
		 * 
		 * <code>
		 * String fileName = "1.txt";
		 * response.setContentType("application/x-msdownload"); 
		 * response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
		 * 
		 * OutputStream out = response.getOutputStream();
		 * InputStream in = new FileInputStream(fileName);
		 * 
		 * byte[] buffer = new byte[1024];
		 * int length = 0;
		 * while((length = in.read(buffer)) != -1){
		 *     out.write(buffer, 0, length);
		 * }
		 * 
		 * in.close();
		 * </code>
		 */
	}
}
